/*

	Simple stubbing

*/
var stub = function(orig, func){
	
	// store the original
	func.orig = orig;
	
	// return the new function
	return func;
	
};
var unstub = function(func) {
	return func.orig;
};



/*

	Stubbed methods

*/
var sbshow_id = null; // used to store the panel ID passed to stubbed.sbshow
var stubQuery = ""; // used to specify explicit querystring
var sbscript_path = null;
var stubbedStatus = "", stubbedTitle = ""; // stores the status and title
var test_element_values = null, counter = counter2 = 0;
var stubbed_dom_element = null;

var stubbed = {
	sbquery: function(){
		return stubQuery;
	},
	sbshow: function(id) {
		sbshow_id = id;
	},
	sbscript: function(s) {
		sbscript_path = s;
	},
	sbstatus: function(s) {
		stubbedStatus = s;
	},
	sbtitle: function(t) {
		stubbedTitle = t;
	},
	
	counter: function() {
		counter++;
	},
	counter2: function() {
		counter2++;
	},
	sbget: function(id) {
		
		stubbed_dom_element = document.createElement("div");
		
		// return new DOM element
		return stubbed_dom_element;
		
	},
	sbval: function(k, v) {
		
		test_element_values = test_element_values || {};
		test_element_values[k] = v;
		
	}
}


/*
	Test objects
*/
var test_one_call_count = 0;
var test_project = {
	title: "Test Proj",
	description: "This is my test proj",
	reps: 10,
	iterations: 2,
	tests: [
		{
			title: "Test One",
			description: "This is my test",
			code: function() {
				test_one_call_count++;
			}
		},
		{
			title: "Test Two",
			description: "This is my test",
			code: function() {
				test_one_call_count++;
			}
		},
		{
			title: "Test Three",
			description: "This is my test",
			code: function() {
				test_one_call_count++;
			}
		}
	]
};


/*

	Helper methods

*/
module("Helper methods");

test("do_something should exist", function(){

	expect(3);

	ok(typeof do_something != "undefined", "do_something should be a function.");
	ok(do_something, "do_something should be a function.");
	equals(typeof do_something, "function", "do_something() should be a function.");

});

test("sbround.  ", function(){
	
	expect(10);
	
	equals(sbround(1), 1, "sbround() should round to three decimal places.");
	equals(sbround(1.1), 1.1, "sbround() should round to three decimal places.");
	equals(sbround(1.12), 1.12, "sbround() should round to three decimal places.");
	equals(sbround(1.123), 1.123, "sbround() should round to three decimal places.");
	equals(sbround(1.1234), 1.1234, "sbround() should round to three decimal places.");
	equals(sbround(1.12345), 1.1234, "sbround() should round to three decimal places.");
	equals(sbround(1.123456), 1.1234, "sbround() should round to three decimal places.");
	equals(sbround(1.1234567), 1.1234, "sbround() should round to three decimal places.");
	equals(sbround(1.12345678), 1.1234, "sbround() should round to three decimal places.");
	equals(sbround(1.123456789), 1.1234, "sbround() should round to three decimal places.");
	
});


/*

	URL parsing

*/
module("URL");

test("sbdeserialize - turns a querystring into an object", function(){
	
	expect(4);
	
	var o = sbdeserialize("p=myproject.js&s=2&r=1,2,3");
	
	equals(o.p, "myproject.js", "p was not correctly extracted");
	equals(o.s, "2", "s was not correctly extracted");
	equals(o.r, "1,2,3", "r was not correctly extracted");
	
	ok(sbdeserialize(""), "Deserializing an empty string should yield an empty object (not null or undefined)");
	
});

test("sbserialize - turns an object into a querystring", function(){

	expect(2);

	var o = {
		p: "myproject.js",
		s: 3,
		r: "1,2,3"
	};
	
	equals(sbserialize(o), "p=myproject.js&s=3&r=1%2C2%2C3", "Object should be correctly serialised into querystring");
	
	equals(sbserialize({}), "", "Empty object should yeild empty string.");
	
});


/*

	Workflow

*/
module("Workflow");


test("sbmain - ", function(){
	
	expect(2);
	
	sbquery = stub(sbquery, stubbed.sbquery);
	sbshow = stub(sbshow, stubbed.sbshow);
	sbget = stub(sbget, stubbed.sbget);
	
	stubQuery = "";
	
	// sbcurrentstate should start off -1
	equals(sbcurrentstate, -1, "sbcurrentstate should start off -1")
	
	sbmain();
	
	// make sure the sbstate was properly setup
	equals(sbcurrentstate, sbstart, "sbcurrentstate should be 'sbstart' once sbmain() is called.");
	
	// UNSTUB: put things back
	sbquery = unstub(sbquery);
	sbshow = unstub(sbshow);
	sbget = unstub(sbget);
	
});

test("sbloadproject - correct call to sbscript", function(){
	
	expect(2);
	
	// STUB
	sbscript = stub(sbscript, stubbed.sbscript);
	sbstatus = stub(sbstatus, stubbed.sbstatus);
	
	var project_path = "my_project.js";

	sbloadproject(project_path);
	
	equals(sbscript_path, project_path, "sbmain() should add correct script")
	
	equals(stubbedStatus, "Loading project my_project.js", "sbstatus() should be set.");
	
	// UNSTUB
	sbscript = unstub(sbscript);
	sbstatus = unstub(sbstatus);
	
});

test("silobox() - project loaded and ready to be viewed", function(){
	
	expect(3);
	
	// set the args and state
	sbcurrentstate = sbviewproj;
	
	// stub  sbshow_view_project
	sbshow_project_summary = stub(sbshow_project_summary, stubbed.counter);	
	sbshow_project_details = stub(sbshow_project_details, stubbed.counter2);
	counter = 0;
	
	silobox(test_project);
	
	sbshow_project_summary = unstub(sbshow_project_summary);
	sbshow_project_details = unstub(sbshow_project_details);
	
	equals(sbproject, test_project, "")
	equals(counter, 1, "sbshow_view_project should be called once.")
	equals(counter2, 1, "sbshow_project_details should be called once.")
	
});

test("silobox() - first test is running", function(){

	expect(4);

	// set the state
	sbcurrentstate = sbrunning;

	var status = "", scheduled_proj = null, scheduled_args = null;

	// stub
	sbstatus = stub(sbstatus, function(s){
		status = s;
	});
	sbtitle = stub(sbtitle, stubbed.sbtitle);
	
	sbscheduleruntest = stub(sbscheduleruntest, function(proj, args){
		scheduled_proj = proj;
		scheduled_args = args;
	});
	
	// set the arguments
	sbargs = {
		p: "project",
		s: 1
	};
	
	// make the call
	silobox(test_project);
	
	equals(status, "1 of 3 - Benchmarking test 'Test One'", "Status should be updated.");
	equals(stubbedTitle, "1 of 3 - Test One");
	equals(scheduled_proj, sbproject, "sbscheduleruntest expected to be passed sbproject.");
	equals(scheduled_args, sbargs, "sbscheduleruntest expected to be passed sbargs.");
		
	// unstub
	sbstatus = unstub(sbstatus);
	sbtitle = unstub(sbtitle);
	sbscheduleruntest = unstub(sbscheduleruntest);

});

test("silobox() - second test is running", function(){

	expect(4);

	// set the state
	sbcurrentstate = sbrunning;

	var status = "", scheduled_proj = null, scheduled_args = null;

	// stub
	sbstatus = stub(sbstatus, function(s){
		status = s;
	});
	sbtitle = stub(sbtitle, stubbed.sbtitle);
	sbscheduleruntest = stub(sbscheduleruntest, function(proj, args){
		scheduled_proj = proj;
		scheduled_args = args;
	});
	
	// set the arguments
	sbargs = {
		p: "project",
		s: 2,
		r: "1"
	};
	
	// make the call
	silobox(test_project);
	
	equals(status, "2 of 3 - Benchmarking test 'Test Two'", "Status should be updated.");
	equals(stubbedTitle, "2 of 3 - Test Two");
	equals(scheduled_proj, sbproject, "sbscheduleruntest expected to be passed sbproject.");
	equals(scheduled_args, sbargs, "sbscheduleruntest expected to be passed sbargs.");
	
	// unstub
	sbstatus = unstub(sbstatus);
	sbscheduleruntest = unstub(sbscheduleruntest);


});

test("silobox() - third test is running", function(){

	expect(4);

	// set the state
	sbcurrentstate = sbrunning;

	var status = "", scheduled_proj = null, scheduled_args = null;

	// stub
	sbstatus = stub(sbstatus, function(s){
		status = s;
	});
	sbtitle = stub(sbtitle, stubbed.sbtitle);
	sbscheduleruntest = stub(sbscheduleruntest, function(proj, args){
		scheduled_proj = proj;
		scheduled_args = args;
	});
	
	// set the arguments
	sbargs = {
		p: "project",
		s: 3,
		r: "1,2"
	};
	
	// make the call
	silobox(test_project);
	
	equals(status, "3 of 3 - Benchmarking test 'Test Three'", "Status should be updated.");
	equals(stubbedTitle, "3 of 3 - Test Three");
	equals(scheduled_proj, sbproject, "sbscheduleruntest expected to be passed sbproject.");
	equals(scheduled_args, sbargs, "sbscheduleruntest expected to be passed sbargs.");
	
	// unstub
	sbstatus = unstub(sbstatus);
	sbscheduleruntest = unstub(sbscheduleruntest);


});

test("silobox() - tests have finished - show results", function(){ 

	expect(6);

	// counters
	var sbshow_project_summary_counter = 0, 
		sbshow_project_details_counter = 0, 
		sbshow_results_counter = 0
		
		sbshow_project_summary_args = null, 
		sbshow_project_details_args = null, 
		sbshow_results_args = null
		
	;

	// set the state
	sbcurrentstate = sbresults;
	
	// stub
	sbshow_project_summary = stub(sbshow_project_summary, function(){
		sbshow_project_summary_counter++;
		sbshow_project_summary_args = arguments;
	});
	sbshow_project_details = stub(sbshow_project_details, function(){
		sbshow_project_details_counter++;
		sbshow_project_details_args = arguments;
	});
	sbshow_results = stub(sbshow_results, function(){
		sbshow_results_counter++;
		sbshow_results_args = arguments;
	});
	
	// set the arguments
	sbargs = {
		p: "project",
		r: "1,2,3,4"
	};
	
	silobox(test_project);
	
	// make assertions
	equals(sbshow_project_summary_counter, 1, "sbshow_project_summary should be called once.");
	equals(sbshow_project_details_counter, 0, "sbshow_project_details should not be called.");
	equals(sbshow_results_counter, 1, "sbshow_project_summary should be called once.");
	
	equals(sbproject, sbshow_project_summary_args[0], "sbshow_project_summary( sbproject, sbargs ) should be called.");
	equals(sbproject, sbshow_results_args[0], "sbshow_results( sbproject, sbargs ) should be called.");
	equals(sbargs, sbshow_results_args[1], "sbshow_results( sbproject, sbargs ) should be called.");
	
	// unstub
	sbshow_project_summary = unstub(sbshow_project_summary);

});

test("sbsetprojectdefaults() - setting default project settings", function(){
	
	// setup our own defaults to properly test the sbsetprojectdefaults method
	sbdefaultreps = 10;
	sbdefaultiterations = 100;
	sbdefaulttestdelay = 250;
	sbdefaulttitle = "Untitled Test";
	
	var p = {};
	
	p = sbsetprojectdefaults(p);
	
	equals(p.reps, sbdefaultreps);
	equals(p.iterations, sbdefaultiterations);
	equals(p.testdelay, sbdefaulttestdelay);
	equals(p.title, sbdefaulttitle);
	equals(p.tests.length, 0);
	
});

/*
	Benchmarking
*/
module("Benchmarking");

test("sbruntest - runs a test", function(){
	
	expect(1);
	
	// clear the test counter
	test_one_call_count = 0;
	
	// setup options and arguments
	var options = test_project;
	var args = {
		p: "project-name",
		s: 1 // first test
	};
	
	args = sbruntest(options, args);
	
	equals(test_one_call_count, 20, "'code' of test should be called 20 times.")
	
});

test("sbaverage - Calculates the average time result", function(){
	
	expect(4);
	
	equals(sbaverage(100, 10), 10);
	equals(sbaverage(100, 5), 20);
	equals(sbaverage(100, 100), 1);
	equals(sbaverage(100, 20), 5);
	
});

test("sbaddresult - Adds the result to the result string", function(){
	
	expect(3);
	
	equals(sbaddresult(null, 1.12), "1.12", "Adding to null should work");
	equals(sbaddresult("1.12", 2.23), "1.12,2.23", "Adding a second result should also insert comma");
	equals(sbaddresult("1.12,2.23", 3.34), "1.12,2.23,3.34", "Adding a third result should also insert comma");
	
});



/*

	States

*/
module("State");
test("state values - ensure state variables contain the correct values", function(){
	
	expect(4);
	
	equals(sbstart, 0, "sbstart");
	equals(sbviewproj, 1, "sbviewproj");
	equals(sbrunning, 2, "sbrunning");
	equals(sbresults, 3, "sbresults");
	
});

test("sbstate - Get state given various options", function(){

	// we want to pass in the Silobox options and get back the
	// 'state' of the application.  Options will come from the
	// arguments in the querystring initially
	
	expect(5);
	
	equals(sbstate({  }), sbstart, "No arguments is 'sbstart'");
	equals(sbstate({ p:"project-path" }), sbviewproj, "p argument means we can see the project (sbviewproj)");
	equals(sbstate({ p:"project-path", s: 1 }), sbrunning, "presence of an s argument means we are running the tests (sbrunning)");
	equals(sbstate({ p:"project-path", s: 1, r: "1.22" }), sbrunning, "presence of an r (with s) argument means we are still running the tests (sbrunning)");
	equals(sbstate({ p:"project-path", r: "1.22" }), sbresults, "absence of s argument but presence of an r argument means we are finished running tests and are showing results (sbresults)");
	
});



/*
 	Script Management

*/
var sb_test_variable = 1;

module("Scripts");
test("sbscript - loading scripts (part I)", function(){

	expect(1);
	
	equals(sb_test_variable, 1, "sb_test_variable not setup correctly initially.")

	// using sbscript - include the test script
	sbscript("tests/sbscript-scripts/modify_variable.js");
	
	// wait for the script to load
	stop();
	
});

test("sbscript - loading scripts (part II)", function(){
	
	expect(1);
	
	equals(sb_test_variable, 10, "sb_test_variable should be changed (to 10) after call to sbscript");
	
});



/*

	DOM Management

*/
module("DOM");
test("sbhead - getting the document head", function() {
	
	expect(1);
	
	equals(document.getElementsByTagName("head")[0], sbhead());
	
});

test("sbhead - document head is cached first time", function() {

	expect(2);
	equals(sbhead(), document.getElementsByTagName("head")[0]);
	
	// override the cache
	var fakeHead = {};
	sbheadcache = fakeHead;
	
	equals(sbhead(), fakeHead)
	
});


/*
	VIEWS
*/
module("Views");


test("sbshow_request_project - shows correct panel", function(){
	
	expect(1);
	
	// stub sbshow
	sbshow = stub(sbshow, stubbed.sbshow);
	sbget = stub(sbget, stubbed.sbget);
	
	// call the method
	sbshow_request_project();
	
	// make sure the right arguments were passed to sbshow
	equals(sbshow_id, "request-project-panel", "sbshow_request_project should call sbshow('sbshow_requestProject')");
	
	// unstub sbshow
	sbshow = unstub(sbshow);
	sbget = unstub(sbget);
	
});

test("sbstatus - sets status", function(){
	
	expect(1);
	
	var statusElement = document.createElement("div");
	
	sbget = stub(sbget, function(){
		return statusElement;
	});
	
	var newStatus = "Successfully set status";

	statusElement.innerHTML = "";
	
	sbstatus(newStatus);
	
	ok(statusElement.innerHTML.substring(0, newStatus.length) == newStatus, "sbstatus() should update innerHTML of #status element.")
	
	sbget = unstub(sbget);
	
});

test("sbshow_project_summary - shows project summary information", function(){
	
	// STUB
	sbval = stub(sbval, stubbed.sbval);
	sbshow = stub(sbshow, stubbed.sbshow);
	sbstatus = stub(sbstatus, stubbed.sbstatus);
	
	test_element_values = {
		status: "Loading..."
	};
	
	// call the method
	sbshow_project_summary(test_project);
	
	equals(stubbedStatus, "", "sbstatus('') should be called");
	equals(test_element_values["project-title"], test_project.title);
	equals(test_element_values["project-description"], test_project.description);
	equals(test_element_values["project-reps"], test_project.reps);
	equals(test_element_values["project-iterations"], test_project.iterations);
	equals(test_element_values["project-test-count"], test_project.tests.length, "project.tests.length");
	equals(sbshow_id, "project-summary", "#project-summary should be made visible")
	
	// UNSTUB
	sbval = unstub(sbval);
	sbshow = unstub(sbshow);
	sbstatus = unstub(sbstatus);
	
});





